package com.my.study.guava.retry;

import com.github.rholder.retry.*;
import com.google.common.base.Predicates;
import com.my.study.bean.User;
import org.junit.Test;

import java.util.Objects;
import java.util.concurrent.Callable;
import java.util.concurrent.ExecutionException;
import java.util.concurrent.TimeUnit;
import java.util.concurrent.atomic.AtomicInteger;
import java.util.concurrent.atomic.AtomicReference;

/**
 * @author: yidujun
 * @create: 2021/07/20 13:21
 */
public class RetryBuilderTest {

    @Test
    public void test01() throws ExecutionException, RetryException {

        //定义重试机制
        Retryer<Boolean> retryer = RetryerBuilder.<Boolean>newBuilder()
                //retryIf 重试条件
                .retryIfException()
                .retryIfRuntimeException()
                .retryIfExceptionOfType(Exception.class)
                .retryIfException(Predicates.equalTo(new Exception()))
                .retryIfResult(Predicates.equalTo(false))

                //等待策略：每次请求间隔1s
                .withWaitStrategy(WaitStrategies.fixedWait(1, TimeUnit.SECONDS))

                //停止策略 : 尝试请求6次
                .withStopStrategy(StopStrategies.stopAfterAttempt(6))

                //时间限制 : 某次请求不得超过2s , 类似: TimeLimiter timeLimiter = new SimpleTimeLimiter();
                .withAttemptTimeLimiter(AttemptTimeLimiters.fixedTimeLimit(2, TimeUnit.SECONDS))

                .build();

        //定义请求实现
        Callable<Boolean> callable = new Callable<Boolean>() {
            int times = 1;

            @Override
            public Boolean call() throws Exception {
                System.out.println("call times = " + times);
                times++;
                if (times == 2) {
                    throw new NullPointerException();
                } else if (times == 3) {
                    throw new Exception();
                } else if (times == 4) {
                    throw new RuntimeException();
                } else if (times == 5) {
                    return false;
                } else {
                    return true;
                }
            }
        };

        Boolean call = retryer.call(callable);
    }


    @Test
    public void customRetryTest() {
        AtomicReference<User> user = new AtomicReference<>();

        System.out.println(user.get());

        Retryer<Boolean> retryer = RetryerBuilder.<Boolean>newBuilder()
                .retryIfException()
                .retryIfExceptionOfType(Exception.class)
                .retryIfResult(res -> Objects.equals(res, Boolean.FALSE))
                // 等待策略
                .withWaitStrategy(WaitStrategies.fibonacciWait(1000,10 * 1000, TimeUnit.MILLISECONDS))
//                .withWaitStrategy(new MyWaitStrategy())
                // 停止策略
                .withStopStrategy(StopStrategies.stopAfterAttempt(6))
                // 监听器
                .withRetryListener(new CustomRetryListener())
                //时间限制 : 某次请求不得超过3s
                .withAttemptTimeLimiter(AttemptTimeLimiters.fixedTimeLimit(3, TimeUnit.SECONDS))
                .build();


        AtomicInteger no = new AtomicInteger(0);
        Callable<Boolean> task = () -> {
            int number = no.getAndIncrement();
            System.out.println(number);
            User res = getUser(number);
            if (null != res) {
                user.set(res);
                return true;
            }

            return false;
        };

        System.out.println(System.currentTimeMillis());
        try {
            retryer.call(task);
        } catch (ExecutionException | RetryException e) {
            e.printStackTrace();
        }

        System.out.println(System.currentTimeMillis());

        System.out.println(user);
    }

    private User getUser(int n) {
        if (n == 3) {
            return new User((long) n, "ydj");
        }
        if (n == 2) {
            throw new RuntimeException("xxx");
        }
        return null;
    }


    /**
     * 自定义等待策略
     * 第一次等待1s
     * 第二次等待2s
     * 之后的全部等待5s
     */
    private static class MyWaitStrategy implements WaitStrategy {

        @Override
        public long computeSleepTime(Attempt failedAttempt) {
            long number = failedAttempt.getAttemptNumber();
            if (number == 1) {
                return 1000;
            }
            if (number == 2) {
                return 2 * 1000;
            }
            return 5 * 1000;
        }
    }


    private static class CustomRetryListener implements RetryListener {

        @Override
        public <V> void  onRetry(Attempt<V> attempt) {
            System.out.println("重试次数："  + attempt.getAttemptNumber());
            if (attempt.hasException()) {
                System.out.println("重试失败: " + attempt.getExceptionCause().getMessage());
            }
            if (attempt.hasResult()) {
                System.out.println("重试结果：" + attempt.getResult());
            }
        }
    }
}
